﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using HalconDotNet;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Collections.ObjectModel;

using STC_Object = System.IntPtr;
using STC_DataSet = System.IntPtr; 


namespace BasicDemo
{
    public partial class Form1 : Form
    {
        private static readonly object Lock = new object();
        static IntPtr m_handle = IntPtr.Zero;
        UInt32 m_nDevNum = 0;
        MV3D_RGBD_DEVICE_INFO_VECTOR m_stVector;

        bool m_bGrabbing = false;
        Thread m_hReceiveThread = null;

        // 本地存储用图
        static MV3D_RGBD_IMAGE_DATA m_stImageInfo = new MV3D_RGBD_IMAGE_DATA(); //图片参数信息
        static UInt32 m_MaxImageSize = 1024 * 1024 * 30;
        static byte[] m_pcDataBuf = new byte[m_MaxImageSize];

        public Form1()
        {
            InitializeComponent();
            DeviceListAcq();
            Control.CheckForIllegalCrossThreadCalls = false;
            pictureBox1.Show();
        }

        //将Byte转换为结构体类型
        public static object ByteToStruct(byte[] bytes, Type type)
        {
            int size = Marshal.SizeOf(type);
            if (size > bytes.Length)
            {
                return null;
            }
            //分配结构体内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将byte数组拷贝到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //将内存空间转换为目标结构体
            object obj = Marshal.PtrToStructure(structPtr, type);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            return obj;
        }

        // ch:显示错误信息 | en:Show error message
        private void ShowErrorMsg(string csMessage, int nErrorNum)
        {
            string errorMsg;
            if (nErrorNum == 0)
            {
                errorMsg = csMessage;
            }
            else
            {
                errorMsg = csMessage + ": Error =" + String.Format("{0:X}", nErrorNum);
            }

            switch ((uint)nErrorNum)
            {
                case Mv3dRgbdSDK.MV3D_RGBD_E_HANDLE: errorMsg += " Error or invalid handle "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_SUPPORT: errorMsg += " Not supported function "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_BUFOVER: errorMsg += " Cache is full "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_CALLORDER: errorMsg += " Function calling order error "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_PARAMETER: errorMsg += " Incorrect parameter "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_RESOURCE: errorMsg += " Applying resource failed "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_NODATA: errorMsg += " No data "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_PRECONDITION: errorMsg += " Precondition error, or running environment changed "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_VERSION: errorMsg += " Version mismatches "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_NOENOUGH_BUF: errorMsg += " Insufficient memory "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_ABNORMAL_IMAGE: errorMsg += " error image "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_LOAD_LIBRARY: errorMsg += " load dll  error "; break;
                case Mv3dRgbdSDK.MV3D_RGBD_E_UNKNOW: errorMsg += " Unknown error "; break;
            }

            MessageBox.Show(errorMsg, "PROMPT");
        }

        private void bnEnum_Click(object sender, EventArgs e)
        {
            DeviceListAcq();
        }

        private void DeviceListAcq()
        {
            // ch:创建设备列表 | en:Create Device List
            System.GC.Collect();
            cbDeviceList.Items.Clear();
            m_nDevNum = 0;
            int nRet = Mv3dRgbdSDK.MV3D_RGBD_GetDeviceNumber(Mv3dRgbdSDK.DeviceType_Ethernet | Mv3dRgbdSDK.DeviceType_USB, ref m_nDevNum);

            m_stVector = new MV3D_RGBD_DEVICE_INFO_VECTOR((int)m_nDevNum);
            for (UInt32 i = 0; i < m_nDevNum; i++)
            {
                m_stVector.Add(new MV3D_RGBD_DEVICE_INFO());
            }

            // 获取网络中设备信息
            nRet = Mv3dRgbdSDK.MV3D_RGBD_GetDeviceList(Mv3dRgbdSDK.DeviceType_Ethernet | Mv3dRgbdSDK.DeviceType_USB, m_stVector[0], m_nDevNum, ref m_nDevNum);
            if (0 != nRet)
            {
                ShowErrorMsg("Enumerate devices fail!", nRet);
                return;
            }

            // ch:在窗体列表中显示设备名 | en:Display device name in the form list
            for (int i = 0; i < m_nDevNum; i++)
            {
                string strSerialNumber = m_stVector[i].chSerialNumber ;// System.Text.Encoding.Default.GetString(m_stVector[i].chSerialNumber);
                strSerialNumber =  strSerialNumber.TrimEnd('\0');

                string strCurrentIp = m_stVector[i].get_netinfo().chCurrentIp;
                strCurrentIp = strCurrentIp.TrimEnd('\0');

                string strModelName = m_stVector[i].chModelName;
                strModelName = strModelName.TrimEnd('\0');

                cbDeviceList.Items.Add("GEV:" + strSerialNumber + " " + strCurrentIp + " (" + strModelName + ")");
            }

            // ch:选择第一项 | en:Select the first item
            if (m_nDevNum != 0)
            {
                cbDeviceList.SelectedIndex = 0;
            }
        }

        private void SetCtrlWhenOpen()
        {
            bnOpen.Enabled = false;

            bnClose.Enabled = true;
            bnStartGrab.Enabled = true;
            bnStopGrab.Enabled = false;
            tbExposure.Enabled = true;
            tbGain.Enabled = true;
            bnGetParam.Enabled = true;
            bnSetParam.Enabled = true;
            bnEnum.Enabled = false;
        }

        private void bnOpen_Click(object sender, EventArgs e)
        {
            if (m_nDevNum == 0 || cbDeviceList.SelectedIndex == -1)
            {
                ShowErrorMsg("No device, please select", 0);
                return;
            }

            // ch:打开设备 | en:Open device
            int nRet = Mv3dRgbdSDK.MV3D_RGBD_OpenDeviceBySerialNumber(ref m_handle, m_stVector[cbDeviceList.SelectedIndex].chSerialNumber);
            if (0 != nRet)
            {
                ShowErrorMsg("Open Device Failed ", nRet);
                return;
            }

            bnGetParam_Click(null, null);// ch:获取参数 | en:Get parameters

            // ch:控件操作 | en:Control operation
            SetCtrlWhenOpen();
        }

        private void SetCtrlWhenClose()
        {
            bnOpen.Enabled = true;

            bnClose.Enabled = false;
            bnStartGrab.Enabled = false;
            bnStopGrab.Enabled = false;
            tbExposure.Enabled = false;
            tbGain.Enabled = false;
            bnGetParam.Enabled = false;
            bnSetParam.Enabled = false;
            bnEnum.Enabled = true;
            
            SaveRawBtn.Enabled = false;
            SaveTiffBtn.Enabled = false;
            SaveBmpBtn.Enabled = false;
            SaveJpgBtn.Enabled = false;
            HalconSave.Enabled = false;
        }

        private void bnClose_Click(object sender, EventArgs e)
        {
            // ch:取流标志位清零 | en:Reset flow flag bit
            if (m_bGrabbing == true)
            {
                m_bGrabbing = false;
                m_hReceiveThread.Join();
            }

            // 清空残留图片
            this.pictureBox1.Image = null;

            // ch:关闭设备 | en:Close Device
            Mv3dRgbdSDK.MV3D_RGBD_Stop(m_handle);
            Mv3dRgbdSDK.MV3D_RGBD_CloseDevice(ref m_handle);

            // ch:控件操作 | en:Control Operation
            SetCtrlWhenClose();
        }


        private void SetCtrlWhenStartGrab()
        {
            bnStartGrab.Enabled = false;
            bnStopGrab.Enabled = true;

            SaveRawBtn.Enabled = true;
            SaveTiffBtn.Enabled = true;
            SaveBmpBtn.Enabled = true;
            SaveJpgBtn.Enabled = true;
            HalconSave.Enabled = true;
        }

        public void ReceiveThreadProcess()
        {
            int nRet = (int)Mv3dRgbdSDK.MV3D_RGBD_OK;

            while ((m_bGrabbing) && (this.Visible))
            {
                MV3D_RGBD_FRAME_DATA stFrameData = new MV3D_RGBD_FRAME_DATA();
                nRet = Mv3dRgbdSDK.MV3D_RGBD_FetchFrame(m_handle, stFrameData, 1000);
                if (0 == nRet)
                {
                    IntPtr hWnd = pictureBox1.Handle;
                    Int32 i = 0;
                    Mv3dRgbdSDK.MV3D_RGBD_DisplayImage(m_handle, stFrameData.stImageData[i], hWnd);  //目前渲染深度图
                    //for (Int32 i = 0; i < stFrameData.nImageCount; i++)
                    {
                        {
                            Monitor.Enter(Lock);
                            m_stImageInfo.nWidth = stFrameData.stImageData[i].nWidth;
                            m_stImageInfo.nHeight = stFrameData.stImageData[i].nHeight;
                            m_stImageInfo.enImageType = stFrameData.stImageData[i].enImageType;
                            m_stImageInfo.nDataLen = stFrameData.stImageData[i].nDataLen;
                            m_stImageInfo.nFrameNum = stFrameData.stImageData[i].nFrameNum;
                            m_stImageInfo.pData = Marshal.UnsafeAddrOfPinnedArrayElement(m_pcDataBuf, 0);

                            if (m_MaxImageSize < stFrameData.stImageData[i].nDataLen)
                            {
                                m_pcDataBuf = new byte[stFrameData.stImageData[i].nDataLen];
                                m_MaxImageSize = stFrameData.stImageData[i].nDataLen;
                            }

                            Marshal.Copy(stFrameData.stImageData[i].pData, m_pcDataBuf, 0, (int)stFrameData.stImageData[i].nDataLen);
                            Monitor.Exit(Lock);
                        }
                    }
                }
            }
        }

        private void bnStartGrab_Click(object sender, EventArgs e)
        {
            // ch:标志位置位true | en:Set position bit true
            m_bGrabbing = true;

            m_hReceiveThread = new Thread(ReceiveThreadProcess);
            m_hReceiveThread.Start();

            // ch:开始采集 | en:Start Grabbing
            int nRet = Mv3dRgbdSDK.MV3D_RGBD_Start(m_handle);
            if (0 != nRet)
            {
                m_bGrabbing = false;
                m_hReceiveThread.Join();
                ShowErrorMsg("Start Grabbing Fail!", nRet);
                return;
            }

            // ch:控件操作 | en:Control Operation
            SetCtrlWhenStartGrab();

        }



        private void bnTriggerExec_Click(object sender, EventArgs e)
        {
            // ch:触发命令 | en:Trigger command
            int nRet = Mv3dRgbdSDK.MV3D_RGBD_SoftTrigger(m_handle);
            if (0 != nRet)
            {
                ShowErrorMsg("Trigger Software Fail!", nRet);
            }
        }

        private void SetCtrlWhenStopGrab()
        {
            bnStartGrab.Enabled = true;
            bnStopGrab.Enabled = false;

            SaveRawBtn.Enabled = false;
            SaveTiffBtn.Enabled = false;
            SaveBmpBtn.Enabled = false;
            SaveJpgBtn.Enabled = false;
            HalconSave.Enabled = false;

        }

        private void bnStopGrab_Click(object sender, EventArgs e)
        {
            // ch:标志位设为false | en:Set flag bit false
            m_bGrabbing = false;
            m_hReceiveThread.Join();

            // ch:停止采集 | en:Stop Grabbing
            int nRet = Mv3dRgbdSDK.MV3D_RGBD_Stop(m_handle);
            if (0 != nRet)
            {
                ShowErrorMsg("Stop Grabbing Fail!", nRet);
            }

            // ch:控件操作 | en:Control Operation
            SetCtrlWhenStopGrab();
        }

        private void bnGetParam_Click(object sender, EventArgs e)
        {
            MV3D_RGBD_PARAM pstValue = new MV3D_RGBD_PARAM();

            pstValue.enParamType = Mv3dRgbdSDK.ParamType_Float;
            MV3D_RGBD_FLOATPARAM floatParam = new MV3D_RGBD_FLOATPARAM();

            int nRet = Mv3dRgbdSDK.MV3D_RGBD_GetParam(m_handle, Mv3dRgbdSDK.MV3D_RGBD_FLOAT_EXPOSURETIME, pstValue);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                ShowErrorMsg("Get Exposure Time Fail!", nRet);
            }
            else
            {
                floatParam = pstValue.get_floatparam();
                tbExposure.Text = floatParam.fCurValue.ToString("F1");
            }
            
            nRet = Mv3dRgbdSDK.MV3D_RGBD_GetParam(m_handle, Mv3dRgbdSDK.MV3D_RGBD_FLOAT_GAIN, pstValue);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                ShowErrorMsg("Get Gain Fail!", nRet);
            }
            else
            {
                floatParam = pstValue.get_floatparam();
                tbGain.Text = floatParam.fCurValue.ToString("F1");
            }

        }

        private void bnSetParam_Click(object sender, EventArgs e)
        {
            try
            {
                float.Parse(tbExposure.Text);
                float.Parse(tbGain.Text);
            }
            catch
            {
                ShowErrorMsg("Please enter correct type!", 0);
                return;
            }
            bool bHasError = false;
            
            MV3D_RGBD_PARAM pstValue = new MV3D_RGBD_PARAM();

            pstValue.enParamType = Mv3dRgbdSDK.ParamType_Float;
            MV3D_RGBD_FLOATPARAM floatParam = new MV3D_RGBD_FLOATPARAM();

            floatParam.fCurValue = float.Parse(tbExposure.Text);
            pstValue.set_floatparam(floatParam);
            int nRet = Mv3dRgbdSDK.MV3D_RGBD_SetParam(m_handle, Mv3dRgbdSDK.MV3D_RGBD_FLOAT_EXPOSURETIME, pstValue);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                bHasError = true;
                ShowErrorMsg("Set Exposure Time Fail!", nRet);
            }

            floatParam.fCurValue = float.Parse(tbGain.Text);
            pstValue.set_floatparam(floatParam);
            nRet = Mv3dRgbdSDK.MV3D_RGBD_SetParam(m_handle, Mv3dRgbdSDK.MV3D_RGBD_FLOAT_GAIN, pstValue);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                bHasError = true;
                ShowErrorMsg("Set Gain Fail!", nRet);
            }

            if (false == bHasError)
            {
                string errorMsg = "Set Para Success!";
                MessageBox.Show(errorMsg, "INFO");
            }

        }

        // 保存图像
        private uint SaveImage(uint nFileType)
        {
            uint nRet = Mv3dRgbdSDK.MV3D_RGBD_OK;
            
            if (!m_bGrabbing)
            {
                ShowErrorMsg("no start work!", 0);
                return Mv3dRgbdSDK.MV3D_RGBD_E_CALLORDER;
            }

            if (0 == m_stImageInfo.nDataLen)
            {
                ShowErrorMsg("no data!", 0);
                return Mv3dRgbdSDK.MV3D_RGBD_E_NODATA;
            }

            Monitor.Enter(Lock);
            nRet = (uint)Mv3dRgbdSDK.MV3D_RGBD_SaveImage(m_handle, m_stImageInfo, nFileType, "");
            Monitor.Exit(Lock);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                ShowErrorMsg("SaveImage failed", 0);
            }

            return nRet;
        }

        // 保存深度图
        private void SaveTiffBtn_Click(object sender, EventArgs e)
        {
            uint nRet = SaveImage(Mv3dRgbdSDK.FileType_TIFF);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                ShowErrorMsg("SaveImage failed!", 0);
                return;
            }

            ShowErrorMsg("Save tiff image success!", 0);
        }

        //保存原始图
        private void SaveRawBtn_Click(object sender, EventArgs e)
        {
            if (!m_bGrabbing)
            {
                ShowErrorMsg("no start work!", 0);
                return;
            }

            if (0 == m_stImageInfo.nDataLen)
            {
                ShowErrorMsg("no data!", 0);
                return;
            }

            string strFileName = "Image_";
            strFileName += m_stImageInfo.nFrameNum;
            strFileName += ".raw";

            FileStream file = new FileStream(strFileName, FileMode.Create, FileAccess.Write);

            {
                Monitor.Enter(Lock);
                file.Write(m_pcDataBuf, 0, (int)m_stImageInfo.nDataLen);
                Monitor.Exit(Lock);
            }

            file.Close();

            ShowErrorMsg("Success save raw file!", 0);
        }

        private void SaveBmpBtn_Click(object sender, EventArgs e)
        {
            uint nRet = SaveImage(Mv3dRgbdSDK.FileType_BMP);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                ShowErrorMsg("SaveImage failed!", 0);
                return;
            }
            
            ShowErrorMsg("Save bmp image success!", 0);
        }

        private void SaveJpgBtn_Click(object sender, EventArgs e)
        {
            uint nRet = SaveImage(Mv3dRgbdSDK.FileType_JPG);
            if (Mv3dRgbdSDK.MV3D_RGBD_OK != nRet)
            {
                ShowErrorMsg("SaveImage failed!", 0);
                return;
            }

            ShowErrorMsg("Save jpg image success!", 0);
        }

        private void HalconSave_Click(object sender, EventArgs e)
        {
            if (!m_bGrabbing)
            {
                ShowErrorMsg("no start work!", 0);
                return;
            }

            if (0 == m_stImageInfo.nDataLen)
            {
                ShowErrorMsg("no data!", 0);
                return;
            }
            if ((Mv3dRgbdSDK.ImageType_Depth == m_stImageInfo.enImageType))
            {
                Monitor.Enter(Lock);

                HObject ho_Image = null;

                {
                    GCHandle hBuf = GCHandle.Alloc(m_pcDataBuf, GCHandleType.Pinned);
                    IntPtr ptr = hBuf.AddrOfPinnedObject();
                    HOperatorSet.GenImage1(out ho_Image, "int2", m_stImageInfo.nWidth, m_stImageInfo.nHeight, ptr.ToInt64());

                    if (hBuf.IsAllocated)
                    {
                        hBuf.Free();
                    }
                }

                if (null != ho_Image)
                {
                    //保存深度图
                    string strtiffName = "./Halcon_Image_";
                    strtiffName += m_stImageInfo.nFrameNum;
                    strtiffName += ".tiff";
                    HOperatorSet.WriteImage(ho_Image, "tiff", 0, strtiffName);

                    ShowErrorMsg("Success save Halcon file!", 0);
                }
                else
                {
                    ShowErrorMsg("Not support save to Halcon file!", 0);
                }

                Monitor.Exit(Lock);
                return;
            }

            if ((Mv3dRgbdSDK.ImageType_RGB8_Planar == m_stImageInfo.enImageType))
            {
                Monitor.Enter(Lock);

                HObject ho_Image = null;

                GCHandle hBuf = GCHandle.Alloc(m_pcDataBuf, GCHandleType.Pinned);
                IntPtr ptr = hBuf.AddrOfPinnedObject();
                HOperatorSet.GenImage3(out ho_Image, "byte", m_stImageInfo.nWidth, m_stImageInfo.nHeight, ptr.ToInt64(), ptr.ToInt64() + m_stImageInfo.nWidth * m_stImageInfo.nHeight, ptr.ToInt64() + m_stImageInfo.nWidth * m_stImageInfo.nHeight * 2);

                if (hBuf.IsAllocated)
                {
                    hBuf.Free();
                }

                if (null != ho_Image)
                {
                    string strtiffName = "./Halcon_Image_";
                    strtiffName += m_stImageInfo.nFrameNum;
                    strtiffName += ".bmp";
                    HOperatorSet.WriteImage(ho_Image, "bmp", 0, strtiffName);

                    ShowErrorMsg("Success save Halcon file!", 0);
                }
                else
                {
                    ShowErrorMsg("Not support save to Halcon file!", 0);
                }

                Monitor.Exit(Lock);
                return;
            }

            return;
        }
    }
}
